package com.googlecode.imanager.client.style;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.googlecode.imanager.client.Control;
import com.googlecode.imanager.client.bridge.style.ITheme;
import com.googlecode.imanager.client.style.listener.StyleAttribute;
import com.googlecode.imanager.client.style.listener.StyleListener;
import com.googlecode.imanager.client.style.listener.StyleWrapper;
import com.googlecode.imanager.client.style.listener.ThemeListener;
import com.googlecode.imanager.client.util.ControlState;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public class Theme implements ITheme, Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private Set<ThemeListener> listeners;
    private Map<String, Map<Integer,StyleField>> wrappers;

    public Theme() {
        super();
        listeners = new HashSet<ThemeListener>();
        wrappers = new HashMap<String, Map<Integer,StyleField>>(32);
    }
    
    public boolean addListener(ThemeListener listener){
        if (listener != null){
            if (listeners.add(listener)){
                listener.onThemeAttach(this);
                return true;
            }
        }
        return false;
    }

    public boolean removeListener(ThemeListener listener){
        if (listener != null){
            if (listeners.remove(listener)){
                listener.onThemeDetach(this);
                return true;
            }
        }
        return false;
    }
    
    public boolean hasListener(ThemeListener listener){
        return listener != null && listeners.contains(listener);
    }
    
    private Map<Integer,StyleField> getStyleMap(String controlClassName){
        Map<Integer,StyleField> map = wrappers.get(controlClassName);
        if (map == null){
            map = new HashMap<Integer, StyleField>(16);
            wrappers.put(controlClassName, map);
        }
        return map;
    }

    public StyleWrapper lookupWrapper(String controlClassName, int stateFlags){

        Map<Integer,StyleField> map = getStyleMap(controlClassName);
        StyleField wrapper = map.get(stateFlags);
        
        if (wrapper == null){
            wrapper = new StyleField(controlClassName, stateFlags);
            map.put(stateFlags, wrapper);
        }
        
        return wrapper;
    }
    
    public StyleWrapper getWrapper(String controlClassName, int stateFlags){
        Map<Integer,StyleField> map = wrappers.get(controlClassName);
        if (map == null){
            return null;
        }
        return map.get(stateFlags);
    }
    
    public Set<String> getRegisteredControlClassNames(){
        return Collections.unmodifiableSet(wrappers.keySet());
    }
    
    public Collection<? extends StyleWrapper> getWrappers(String controlClassName){
        Map<Integer,StyleField> map = wrappers.get(controlClassName);
        if (map == null){
            return Collections.emptySet();
        }
        return Collections.unmodifiableCollection(map.values());
    }
        
    public Style getStyle(String controlClassName, int stateFlags) {
        StyleWrapper wrapper = getWrapper(controlClassName, stateFlags);
        return wrapper == null ? null : wrapper.getStyle();
    }
    
    public Style getStyle(String controlClassName) {
        StyleWrapper wrapper = getWrapper(controlClassName, 0);
        return wrapper == null ? null : wrapper.getStyle();
    }
    
    public void setStyle(Style style, String controlClassName, int stateFlags) {
        lookupWrapper(controlClassName, stateFlags).setStyle(style);
    }
    
    public void setStyle(Style style, String controlClassName) {
        lookupWrapper(controlClassName, 0).setStyle(style);
    }
    
    @Override
    public Style getStyle(Class<? extends Control> controlClass, int stateFlags) {
        return getStyle(controlClass.getName(), stateFlags);
    }

    public void setStyle(Style style, Class<? extends Control> controlClass, int stateFlags) {
        setStyle(style, controlClass.getName(), stateFlags);
    }

    public void setStyle(Style style, Class<? extends Control> controlClass) {
        setStyle(style, controlClass.getName(), 0);
    }

    @Override
    public Style getStyle(Class<? extends Control> controlClass) {
        return getStyle(controlClass.getName(), 0);
    }

    @Override
    public Style getStyle(Class<? extends Control> controlClass, ControlState... states) {
        return getStyle(controlClass.getName(), ControlState.flagsOf(states));
    }

    public void getStyle(Style style, Class<? extends Control> controlClass, ControlState... states) {
        setStyle(style, controlClass.getName(), ControlState.flagsOf(states));
    }

    @Override
    public Style getStyle(String controlClassName, ControlState... states) {
        return getStyle(controlClassName, ControlState.flagsOf(states));
    }

    public void setStyle(Style style, String controlClassName, ControlState... states) {
        setStyle(style, controlClassName, ControlState.flagsOf(states));
    }

    
    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    private class StyleField extends StyleWrapper implements StyleListener {

        private static final long serialVersionUID = 1L;

        private String controlClassName;
        private int stateFlags;

        public StyleField(String controlClassName, int stateFlags) {
            super();
            this.controlClassName = controlClassName;
            this.stateFlags = stateFlags;
        }

        @Override
        public String getControlClassName() {
            return controlClassName;
        }

        @Override
        public int getStateFlags() {
            return stateFlags;
        }

        @Override
        public Control getControl() {
            return null;
        }

        @Override
        public <T> void onStyleAttributeChanged(Style style, StyleAttribute<T> attribute, T attributeValue) {
            for (ThemeListener listener : listeners){
                listener.onStyleAttributeChanged(this, attribute, attributeValue);
            }
        }

        @Override
        public String toString() {
            return "StyleWrapper[theme=" + Theme.this + "]";
        }
        
    }


    @Override
    public String toString() {
        return "Theme[hashCode=" + hashCode() + "]";
    }
    
}
